package com.qqt.csr.im.socketio.utils;

import cn.hutool.extra.spring.SpringUtil;
import com.corundumstudio.socketio.SocketIOClient;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.RemovalCause;
import com.github.benmanes.caffeine.cache.RemovalListener;
import com.qqt.csr.common.utils.CacheKeyUtil;
import com.qqt.csr.common.utils.RedissonLockUtil;
import com.qqt.csr.im.entity.Connection;
import com.qqt.csr.im.service.ConnectionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @Author: yang hao
 * @Date: 2024/10/15 15:08
 * @Description: 本地缓存工具类
 **/
@Slf4j
@Component("socketIOLocalCacheUtil")
public class SocketIOLocalCacheUtil implements InitializingBean {
    private static volatile SocketIOLocalCacheUtil INSTANCE;
    /**
     * socket-io缓存
     */
    private Cache<String, SocketIOClient> CLIENT_CACHE;
    /**
     * 缓存失效时间
     */
    @Value(value = "${socketio.customCache.expireTimeSeconds}")
    private Long expireTimeSeconds;
    /**
     * 最大缓存数量
     */
    @Value(value = "${socketio.customCache.maxSocketSize}")
    private Long maxSocketSize;
    @Autowired
    private RedissonLockUtil redissonLockUtil;
    @Autowired
    private ConnectionService connectionService;

    @Override
    public void afterPropertiesSet() throws Exception {
        INSTANCE = SpringUtil.getBean(SocketIOLocalCacheUtil.class);
        CLIENT_CACHE = Caffeine.newBuilder()
                .maximumSize(maxSocketSize)
                .expireAfterAccess(expireTimeSeconds, TimeUnit.SECONDS)
                .removalListener(new RemovalListener<String, SocketIOClient>() {
                    @Override
                    public void onRemoval(String key, SocketIOClient socketIOClient, RemovalCause cause) {
                        if (RemovalCause.EXPIRED.equals(cause) || RemovalCause.COLLECTED.equals(cause) || RemovalCause.SIZE.equals(cause)) {
                            try {
                                log.info("触发缓存自动清理回调：key:{}", key);
                                redissonLockUtil.distributedLock(() -> {
                                    SocketIOUtil.tryCloseOldSocket(socketIOClient);
                                    connectionService.disconnect(socketIOClient);
                                }, String.format(CacheKeyUtil.IM.UPDATE_CONNECTION_LOCK, key), 60, 30, TimeUnit.SECONDS);
                            } catch (Exception e) {
                                log.error("缓存失效回调异常！key:{}", key, e);
                            }
                        }
                    }
                })
                .build();
    }

    /**
     * SocketIOClient set
     * @param key {@link String} key
     * @param value {@link SocketIOClient} value
     */
    public static void setClient(String key, SocketIOClient value) {
        SocketIOLocalCacheUtil.INSTANCE.CLIENT_CACHE.put(key, value);
    }

    /**
     * SocketIOClient get
     * @param key {@link String} key
     * @return {@link SocketIOClient} socket-io客户端
     */
    public static SocketIOClient getClient(String key) {
        return SocketIOLocalCacheUtil.INSTANCE.CLIENT_CACHE.getIfPresent(key);
    }

    /**
     * SocketIOClient remove
     *
     * @param key {@link String} key
     * @return {@link SocketIOClient} socket-io客户端
     */
    public static void remove(String key) {
        SocketIOLocalCacheUtil.INSTANCE.CLIENT_CACHE.invalidate(key);
    }

}
